/**
 * 
 */
package group.anglers.om.core.persistence.impls;

import group.anglers.om.core.exceptions.OmException;
import group.anglers.om.core.exceptions.UnExpectedException;
import group.anglers.om.core.persistence.Query;
import group.anglers.om.core.persistence.serialization.impl.ModelSerialization;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * @author an.pham
 * 
 */
class QueryImpl<X> implements Query<X> {
	List<Object> queryPos = new ArrayList<Object>();

	private ModelSerialization modelSerialization;
	
	private String sql = null;
	private Connection connection = null;
	private int maxResult = -1;
	
	QueryImpl(Connection connection, String sql, ModelSerialization modelSerialization) {
		this.sql = sql;
		this.connection = connection;
		this.modelSerialization = modelSerialization;
		
	}

	@Override
	public List<X> getResultList() throws OmException {
		List<X> results = new ArrayList<X>();
		
		X result = null;
		
		try {
			ResultSet resultSet = null;
			PreparedStatement prepareStatement = this.connection.prepareStatement(this.sql);
			
			if(maxResult > 0){
				prepareStatement.setMaxRows(maxResult);
			}
			
			int positions = queryPos.size();

			if (positions > 0) {
				for (int i = 0; i < positions; i++) {
					prepareStatement.setObject(i + 1, queryPos.get(i));
				}
			}
			// Execute query statement
			try {
				resultSet = prepareStatement.executeQuery();
				if(resultSet != null){
					while(resultSet.next()){
						/* create an instance */
						result = (X) modelSerialization.getModelClass().newInstance();
						
						SqlUtils.mapToModel(modelSerialization, resultSet, result);
						
						results.add(result);
					}
				}
			} finally {
				if(prepareStatement != null){
					prepareStatement.close();
				}
				
				if(resultSet != null){
					resultSet.close();
				}
			}
		} catch (Exception e) {
			throw new UnExpectedException(e);
		}
		
		return results;
	}

	@SuppressWarnings("unchecked")
	@Override
	public X getSingleResult() throws OmException {
		X result = null;
		try {
			ResultSet resultSet = null;
			PreparedStatement prepareStatement = this.connection.prepareStatement(this.sql);
			int positions = queryPos.size();

			if (positions > 0) {
				for (int i = 0; i < positions; i++) {
					prepareStatement.setObject(i + 1, queryPos.get(i));
				}
			}
			// Execute query statement
			try {
				resultSet = prepareStatement.executeQuery();
				if(resultSet != null){
					if(resultSet.next()){
						/* create an instance */
						result = (X) modelSerialization.getModelClass().newInstance();
						
						SqlUtils.mapToModel(modelSerialization, resultSet, result);
					}
				}
			} finally {
				if(prepareStatement != null){
					prepareStatement.close();
				}
				
				if(resultSet != null){
					resultSet.close();
				}
			}
		} catch (Exception e) {
			throw new UnExpectedException(e);
		}
		return result;
	}

	@Override
	public int executeUpdate() throws OmException {
		try {
			PreparedStatement prepareStatement = this.connection.prepareStatement(this.sql);
			int positions = queryPos.size();

			if (positions > 0) {
				for (int i = 0; i < positions; i++) {
					prepareStatement.setObject(i + 1, queryPos.get(i));
				}
			}
			// Execute query statement
			try {
				return prepareStatement.executeUpdate();
			} finally {
				if(prepareStatement != null){
					prepareStatement.close();
				}
			}
		} catch (Exception e) {
			throw new UnExpectedException(e);
		}
	}

	@Override
	public Query<X> setMaxResults(int maxResult) {
		this.maxResult = maxResult;
		return this;
	}

	@Override
	public Query<X> setParameter(Object param) {
		queryPos.add(param);
		return this;
	}

}
